package zzzhc.image.recognize.nn;

import java.util.Vector;

import org.joone.engine.Layer;
import org.joone.engine.Monitor;
import org.joone.engine.NeuralNetListener;
import org.joone.engine.Pattern;
import org.joone.io.MemoryInputSynapse;
import org.joone.io.MemoryOutputSynapse;
import org.joone.net.NeuralNet;

public abstract class NNBase implements NN {

	protected NeuralNet neuralNet;

	protected MemoryInputSynapse input;

	protected MemoryOutputSynapse output;

	protected Layer inputLayer;

	protected Layer outputLayer;

	protected NNInput nnInput;

	protected TrainerSetting setting;

	protected NeuralNetListener neuralNetListener;

	public NNBase() {
		buildNeuralNet();
	}

	public NeuralNet getNeuralNet() {
		return neuralNet;
	}

	public void setTrainerSetting(TrainerSetting setting) {
		this.setting = setting;

		Monitor monitor = neuralNet.getMonitor();
		monitor.setLearning(true);
		monitor.setTotCicles(setting.getEpochs());

		monitor.setLearningRate(setting.getLearningRate());
		monitor.setMomentum(setting.getMomentum());
	}

	public void addNoise() {
		neuralNet.addNoise(0.2d);
	}

	public void randomize() {
		neuralNet.randomize(0.3d);
	}

	public void setNNInput(NNInput nnInput) {
		if (this.nnInput==nnInput) {
			return;
		}
		this.nnInput = nnInput;
		neuralNet.resetInput();
		neuralNet.getMonitor().setTrainingPatterns(nnInput.getPatterns());
		doSetNNInput();
	}

	protected abstract void doSetNNInput();

	public void setNeuralNetListener(NeuralNetListener neuralNetListener) {
		this.neuralNetListener = neuralNetListener;

		neuralNet.getMonitor().addNeuralNetListener(neuralNetListener);
	}

	public void run() {
		neuralNet.start();
		neuralNet.getMonitor().Go();
	}

	public void continueRun() {
		neuralNet.start();
		neuralNet.getMonitor().runAgain();
	}

	public void pause() {
		neuralNet.getMonitor().Stop();
	}

	@SuppressWarnings("unchecked")
	public TrainResult getTrainResult() {
		NNOutput nnOutput = new NNOutput();
		Vector patterns = output.getAllPatterns();
		for (int i = 0; i < patterns.size(); i++) {
			Pattern p = (Pattern) patterns.get(i);
			nnOutput.addItem(p.getArray());
		}
		nnOutput.assignCode(nnInput);

		NeuralNet cloneNet = neuralNet.cloneNet();
		cloneNet.removeAllInputs();
		cloneNet.removeAllOutputs();
		Monitor monitor = cloneNet.getMonitor();
		monitor.setTrainingPatterns(1);
		monitor.setTotCicles(1);
		monitor.setLearning(false);
		monitor.removeAllListeners();

		TrainResult result = new TrainResult();
		result.setNnOutput(nnOutput);
		result.setNeuralNet(cloneNet);

		return result;
	}

	protected abstract void buildNeuralNet();

	public void train(TrainerSetting setting, NNInput nnInput,
			NeuralNetListener listener) {
		this.nnInput = nnInput;

		neuralNet.start();
		neuralNet.getMonitor().Go();
	}

	@SuppressWarnings("unchecked")
	public TrainResult getResult() {
		NNOutput nnOutput = new NNOutput();
		Vector patterns = output.getAllPatterns();
		for (int i = 0; i < patterns.size(); i++) {
			Pattern p = (Pattern) patterns.get(i);
			nnOutput.addItem(p.getArray());
		}
		nnOutput.assignCode(nnInput);
		return new TrainResult(nnOutput);
	}

	public NeuralNet getTrainedNeuralNet() {
		inputLayer.removeAllInputs();
		outputLayer.removeAllOutputs();
		// net.removeAllInputs();
		// net.removeAllOutputs();
		Monitor monitor = neuralNet.getMonitor();
		monitor.setTrainingPatterns(1);
		monitor.setTotCicles(1);
		monitor.setLearning(false);
		monitor.removeAllListeners();
		NeuralNet returnNet = neuralNet;
		neuralNet = null;
		return returnNet;
	}

}
